import { IMode } from "./IMode";
import { ArmatureController } from "../ArmatureController";
import { Point, Transform } from "pixi.js";
import { Scale as ScaleBar } from "../../views/OperatorBar/Scale";
import { emitter } from "../../../eventEmitter";
import { ItransformableItem } from "../ITransformableItem";


type CursorState = {
    state: "selectedEmpty" | "inSelected" | "inUnSelected" | "inSpace" | "inScaleX" | "inScaleY";
    target: ItransformableItem | null;
};

export class ScaleController implements IMode {
    private _ctrl: ArmatureController;
    private _preDragMovePoint = new Point();
    private _curDragMovePoint = new Point();
    public operateBar: ScaleBar;
    private _cursorState: CursorState = { state: "selectedEmpty", target: null };

    constructor(ctrl: ArmatureController) {
        this._ctrl = ctrl;
        this.operateBar = new ScaleBar();
        this._ctrl.view.addChild(this.operateBar);
        this.operateBar.visible = false;
    }

    private _scaleItem(bone: ItransformableItem, pre: Point, cur: Point, onlyX = false, onlyY = false) {
        let parent = bone.parent;
        if (parent) {
            pre = parent.view.toLocal(pre);
            cur = parent.view.toLocal(cur);
        }

        let dist = Math.sqrt(Math.pow((this._curDragMovePoint.x - this._preDragMovePoint.x), 2) + Math.pow((this._curDragMovePoint.y - this._preDragMovePoint.y), 2));
        let angle = Math.atan2((this._curDragMovePoint.y - this._preDragMovePoint.y), (this._curDragMovePoint.x - this._preDragMovePoint.x));
        let diff = Math.abs(dist * Math.cos(angle)) > Math.abs(dist * Math.sin(angle)) ? dist * Math.cos(angle) : dist * Math.sin(angle);
        let scaleX = diff / 40;
        let scaleY = scaleX;

        if (onlyX) {
            scaleY = 0;
        }

        if (onlyY) {
            scaleX = 0;
        }

        bone.transform.scaleX += scaleX;
        bone.transform.scaleY += scaleY;
        bone.updateMatrixByLocalTransform();
        bone.updateSubItems();
    }

    private _scaleAction(onlyX = false, onlyY = false): void {
        let armatureCtrl = this._ctrl;

        //只操作最高节点
        let items = armatureCtrl.selectedItemManager.getMinItems();
        if (items) {
            for (const item of items) {
                this._scaleItem(item, this._preDragMovePoint, this._curDragMovePoint, onlyX, onlyY);
            }
        }
        this._preDragMovePoint.copyFrom(this._curDragMovePoint);
        this.updateOperateBar(true);
    }

    public onDragMove(event: any): void {
        this._curDragMovePoint.set(event.global.x, event.global.y);
        switch (this._cursorState.state) {
            case "inScaleX":
                this._scaleAction(true, false)
                break;

            case "inScaleY":
                this._scaleAction(false, true)
                break;

            default:
                this._scaleAction();
                break;
        }

    }

    public onDragMoveEnd(event: any): void {

    }

    private _clickHandler() {
        let armatureCtrl = this._ctrl;

        switch (this._cursorState.state) {
            case "inUnSelected":
                if (this._cursorState.target) {
                    armatureCtrl.selectedItemManager.clearSelectedItems();
                    armatureCtrl.selectedItemManager.addSelectItem(this._cursorState.target);
                }

                break;

            default:
                break;
        }
    }

    public onDragStart(event: any): void {
        let point = new Point(event.global.x, event.global.y);
        this.updateCursorState(point);

        //let armatureCtrl = this._ctrl;
        this._preDragMovePoint.set(event.global.x, event.global.y);
        this._clickHandler();
        this.updateCursorState(point);

        this.updateOperateBar(true);
        this._barHoverHandler();
    }

    public getCursorState(position: Point): CursorState {
        const armatureCtrl = this._ctrl;
        let ret: CursorState = {
            state: "selectedEmpty",
            target: null,
        };

        if (armatureCtrl.selectedItemManager.selectedItemsSet.size > 0) {
            if (this.operateBar.containsPoint(position)) {
                ret.state = "inScaleX";
                ret.target = this.operateBar.target;
                return ret;

            } else if (this.operateBar.scaleY.containsPoint(position)) {
                ret.state = "inScaleY";
                ret.target = this.operateBar.target;
                return ret;
            }
        }

        for (const bone of armatureCtrl.boneControllers) {
            if (bone.isContainPoint(position)) {
                if (armatureCtrl.selectedItemManager.selectedItemsSet.has(bone)) {
                    ret.state = "inSelected";
                    ret.target = bone;
                    return ret;
                } else {
                    ret.state = "inUnSelected";
                    ret.target = bone;
                    return ret;
                }
            }
        }

        let displayList = armatureCtrl.getCurDisplayList();
        if (displayList) {
            for (const display of displayList) {
                if (display.isContainPoint(position)) {
                    if (armatureCtrl.selectedItemManager.selectedItemsSet.has(display)) {
                        ret.state = "inSelected";
                        ret.target = display;
                        return ret;
                    } else {
                        ret.state = "inUnSelected";
                        ret.target = display;
                        return ret;
                    }
                }
            }
        }

        if (armatureCtrl.selectedItemManager.selectedItemsSet.size > 0) {
            ret.state = "inSpace";
        }
        return ret;
    }

    public _barHoverHandler() {
        if (this._cursorState.state == "inScaleX") {
            this.operateBar.hoverScaleX();
        } else if (this._cursorState.state == "inScaleY") {
            this.operateBar.hoverScaleY();
        } else {
            this.operateBar.leaveScaleX();
            this.operateBar.leaveScaleY();
        }
    }

    public updateCursorState(position: Point): void {
        const cursorState = this.getCursorState(position);
        if (this._cursorState.state !== cursorState.state || this._cursorState.target !== cursorState.target) {
            this._cursorState.state = cursorState.state;
            this._cursorState.target = cursorState.target;
        }
    }

    private _hoverHandler() {
        let armatureCtrl = this._ctrl;
        let bone = this._cursorState.target;
        if (bone) {
            if (armatureCtrl.hoverItem == null || armatureCtrl.hoverItem !== bone) {
                bone.hoverEnter();
                if (armatureCtrl.hoverItem) {
                    armatureCtrl.hoverItem.hoverLeave();
                }
                armatureCtrl.hoverItem = bone;
            }
        } else {
            if (armatureCtrl.hoverItem) {
                armatureCtrl.hoverItem.hoverLeave();
                armatureCtrl.hoverItem = null;
            }
        }
    }

    public onHover(event: any): void {
        let point = new Point(event.global.x, event.global.y);
        this.updateCursorState(point);
        this._hoverHandler();
        this._barHoverHandler();
    }

    public onEnter(): void {
        this.updateOperateBar(true);
        emitter.emit("changeCursor", "scale");
    }

    public onLeave(): void {
        this.updateOperateBar(false);
    }

    public updateOperateBar(visible: boolean): void {
        let armatureCtrl = this._ctrl;

        if (visible) {
            let operateBone = armatureCtrl.selectedItemManager.getOperateItem();
            console.log("operate bar", operateBone);
            if (operateBone) {
                this.operateBar.visible = true;
                this.operateBar.position.set(operateBone.view.position.x, operateBone.view.position.y);
                this.operateBar.rotation = operateBone.view.rotation;

                let boneTran = Transform.IDENTITY;
                operateBone.view.localTransform.decompose(boneTran);
                this.operateBar.setTransform(this.operateBar.position.x, this.operateBar.position.y, 1, 1, boneTran.rotation, boneTran.skew.x, boneTran.skew.y);

                this.operateBar.updateScale(operateBone.transform.scaleX, operateBone.transform.scaleY);
                this.operateBar.target = operateBone;
            }
        } else {
            this.operateBar.target = null;
            this.operateBar.visible = false;
        }
    }
}